using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Update;

namespace Microsoft.EntityFrameworkCore.Dm.Update.Internal;

public class DmUpdateSqlGenerator : UpdateSqlGenerator, IDmUpdateSqlGenerator, IUpdateSqlGenerator
{
	private const string InsertedTableBaseName = "@inserted";

	private const string ToInsertTableAlias = "i";

	private const string PositionColumnName = "_Position";

	private const string PositionColumnDeclaration = "\"_Position\" int";

	private const string FullPositionColumnName = "i._Position";

	public DmUpdateSqlGenerator([NotNull] UpdateSqlGeneratorDependencies dependencies)
		: base(dependencies)
	{
	}

	public virtual ResultSetMapping AppendBulkInsertOperation(StringBuilder commandStringBuilder, IReadOnlyList<IReadOnlyModificationCommand> modificationCommands, int commandPosition)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		if (modificationCommands.Count == 1 && modificationCommands[0].ColumnModifications.All(delegate(IColumnModification o)
		{
			if (o.IsKey && o.IsRead)
			{
				IProperty property2 = o.Property;
				if (property2 == null)
				{
					return false;
				}
				return ((IReadOnlyProperty)(object)property2).GetValueGenerationStrategy() == DmValueGenerationStrategy.IdentityColumn;
			}
			return true;
		}))
		{
			return ((UpdateSqlGenerator)this).AppendInsertOperation(commandStringBuilder, modificationCommands[0], commandPosition);
		}
		List<IColumnModification> list = modificationCommands[0].ColumnModifications.Where((IColumnModification o) => o.IsRead).ToList();
		List<IColumnModification> list2 = modificationCommands[0].ColumnModifications.Where((IColumnModification o) => o.IsWrite).ToList();
		List<IColumnModification> keyOperations = modificationCommands[0].ColumnModifications.Where((IColumnModification o) => o.IsKey).ToList();
		bool flag = list2.Count == 0;
		List<IColumnModification> list3 = modificationCommands[0].ColumnModifications.Where(delegate(IColumnModification o)
		{
			IProperty property = o.Property;
			return property == null || ((IReadOnlyProperty)(object)property).GetValueGenerationStrategy() != DmValueGenerationStrategy.IdentityColumn;
		}).ToList();
		if (flag)
		{
			if (list3.Count == 0 || list.Count == 0)
			{
				foreach (IReadOnlyModificationCommand modificationCommand in modificationCommands)
				{
					((UpdateSqlGenerator)this).AppendInsertOperation(commandStringBuilder, modificationCommand, commandPosition);
				}
				if (list.Count == 0)
				{
					return (ResultSetMapping)0;
				}
				return (ResultSetMapping)5;
			}
			if (list3.Count > 1)
			{
				list3.RemoveRange(1, list3.Count - 1);
			}
		}
		if (list.Count == 0)
		{
			return AppendBulkInsertWithoutServerValues(commandStringBuilder, modificationCommands, list2);
		}
		if (flag)
		{
			return AppendBulkInsertWithOnlyDefaultValues(commandStringBuilder, modificationCommands, commandPosition, list3, keyOperations, list);
		}
		return AppendBulkInsertWithServerValues(commandStringBuilder, modificationCommands, commandPosition, list2, keyOperations, list);
	}

	private ResultSetMapping AppendBulkInsertWithoutServerValues(StringBuilder commandStringBuilder, IReadOnlyList<IReadOnlyModificationCommand> modificationCommands, List<IColumnModification> writeOperations)
	{
		string tableName = modificationCommands[0].TableName;
		string schema = modificationCommands[0].Schema;
		this.AppendInsertCommandHeader(commandStringBuilder, tableName, schema, (IReadOnlyList<IColumnModification>)writeOperations);
		this.AppendValuesHeader(commandStringBuilder, (IReadOnlyList<IColumnModification>)writeOperations);
		AppendValues(commandStringBuilder, writeOperations, null);
		for (int i = 1; i < modificationCommands.Count; i++)
		{
			commandStringBuilder.Append(",").AppendLine();
			AppendValues(commandStringBuilder, modificationCommands[i].ColumnModifications.Where((IColumnModification o) => o.IsWrite).ToList(), null);
		}
		commandStringBuilder.Append(this.SqlGenerationHelper.StatementTerminator).AppendLine();
		return (ResultSetMapping)0;
	}

	private void AppendSelectIdentity(StringBuilder commandStringBuilder, IReadOnlyList<IReadOnlyModificationCommand> modificationCommands, List<IColumnModification> nonwrite_keys, int commandPosition)
	{
		StringBuilder stringBuilder = commandStringBuilder.Append(" RETURN ").AppendJoin(nonwrite_keys, this.SqlGenerationHelper, delegate(StringBuilder sb, IColumnModification o, ISqlGenerationHelper helper)
		{
			helper.DelimitIdentifier(sb, o.ColumnName);
		}, ",");
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder);
		handler.AppendLiteral(" INTO c");
		handler.AppendFormatted(commandPosition);
		stringBuilder.Append(ref handler).Append(this.SqlGenerationHelper.StatementTerminator).AppendLine();
	}

	private ResultSetMapping AppendBulkInsertWithOnlyDefaultValues(StringBuilder commandStringBuilder, IReadOnlyList<IReadOnlyModificationCommand> modificationCommands, int commandPosition, List<IColumnModification> writeOperations, List<IColumnModification> keyOperations, List<IColumnModification> readOperations)
	{
		if (readOperations != null && readOperations.Count > 0)
		{
			AppendDeclareTable(commandStringBuilder, "@inserted", commandPosition, readOperations, "\"_Position\" int");
		}
		string tableName = modificationCommands[0].TableName;
		string schema = modificationCommands[0].Schema;
		_ = modificationCommands[0].ColumnModifications;
		commandStringBuilder.AppendLine("BEGIN");
		if (readOperations != null && readOperations.Count > 0)
		{
			commandStringBuilder.Append("c").Append(commandPosition).Append(" = NEW rrr")
				.Append(commandPosition)
				.Append("[")
				.Append(modificationCommands.Count.ToString())
				.AppendLine("];");
		}
		this.AppendInsertCommandHeader(commandStringBuilder, tableName, schema, (IReadOnlyList<IColumnModification>)writeOperations);
		this.AppendValuesHeader(commandStringBuilder, (IReadOnlyList<IColumnModification>)writeOperations);
		AppendValues(commandStringBuilder, writeOperations, null);
		for (int i = 1; i < modificationCommands.Count; i++)
		{
			commandStringBuilder.Append(",").AppendLine();
			AppendValues(commandStringBuilder, writeOperations, null);
		}
		if (readOperations != null && readOperations.Count > 0)
		{
			AppendSelectIdentity(commandStringBuilder, modificationCommands, readOperations, commandPosition);
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, commandStringBuilder);
			handler.AppendLiteral(" SELECT * FROM ARRAY c");
			handler.AppendFormatted(commandPosition);
			commandStringBuilder.Append(ref handler);
		}
		commandStringBuilder.Append(this.SqlGenerationHelper.StatementTerminator).AppendLine();
		commandStringBuilder.AppendLine(" END;").AppendLine();
		return (ResultSetMapping)3;
	}

	private ResultSetMapping AppendBulkInsertWithServerValues(StringBuilder commandStringBuilder, IReadOnlyList<IReadOnlyModificationCommand> modificationCommands, int commandPosition, List<IColumnModification> writeOperations, List<IColumnModification> keyOperations, List<IColumnModification> readOperations)
	{
		if (readOperations != null && readOperations.Count > 0)
		{
			AppendDeclareTable(commandStringBuilder, "@inserted", commandPosition, readOperations, "\"_Position\" int");
		}
		string tableName = modificationCommands[0].TableName;
		string schema = modificationCommands[0].Schema;
		_ = modificationCommands[0].ColumnModifications;
		commandStringBuilder.AppendLine("BEGIN");
		if (readOperations != null && readOperations.Count > 0)
		{
			commandStringBuilder.Append("c").Append(commandPosition).Append(" = NEW rrr")
				.Append(commandPosition)
				.Append("[")
				.Append(modificationCommands.Count.ToString())
				.AppendLine("];");
		}
		this.AppendInsertCommandHeader(commandStringBuilder, tableName, schema, (IReadOnlyList<IColumnModification>)writeOperations);
		this.AppendValuesHeader(commandStringBuilder, (IReadOnlyList<IColumnModification>)writeOperations);
		AppendValues(commandStringBuilder, writeOperations, null);
		for (int i = 1; i < modificationCommands.Count; i++)
		{
			commandStringBuilder.Append(",").AppendLine();
			AppendValues(commandStringBuilder, modificationCommands[i].ColumnModifications.Where((IColumnModification o) => o.IsWrite).ToList(), null);
		}
		if (readOperations != null && readOperations.Count > 0)
		{
			AppendSelectIdentity(commandStringBuilder, modificationCommands, readOperations, commandPosition);
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, commandStringBuilder);
			handler.AppendLiteral(" SELECT * FROM ARRAY c");
			handler.AppendFormatted(commandPosition);
			commandStringBuilder.Append(ref handler);
		}
		commandStringBuilder.Append(this.SqlGenerationHelper.StatementTerminator).AppendLine();
		commandStringBuilder.AppendLine(" END;").AppendLine();
		return (ResultSetMapping)3;
	}

	private void AppendValues(StringBuilder commandStringBuilder, IReadOnlyList<IColumnModification> operations, string? additionalLiteral)
	{
		if (operations.Count <= 0)
		{
			return;
		}
		commandStringBuilder.Append("(").AppendJoin(operations, this.SqlGenerationHelper, delegate(StringBuilder sb, IColumnModification o, ISqlGenerationHelper helper)
		{
			if (o.IsWrite)
			{
				helper.GenerateParameterName(sb, o.ParameterName);
			}
			else if (RelationalPropertyExtensions.GetDefaultValueSql((IReadOnlyProperty)(object)o.Property) != null)
			{
				string value = RelationalPropertyExtensions.GetDefaultValueSql((IReadOnlyProperty)(object)o.Property).ToString();
				sb.Append(value);
			}
			else
			{
				sb.Append("DEFAULT");
			}
		});
		if (additionalLiteral != null)
		{
			commandStringBuilder.Append(", ").Append(additionalLiteral);
		}
		commandStringBuilder.Append(")");
	}

	private void AppendDeclareTable(StringBuilder commandStringBuilder, string name, int index, IReadOnlyList<IColumnModification> readOperations, string? additionalColumns = null)
	{
		commandStringBuilder.Append("DECLARE ");
		if (readOperations == null || readOperations.Count <= 0)
		{
			return;
		}
		commandStringBuilder.Append(" TYPE rrr").Append(index).Append(" IS RECORD (")
			.AppendJoin(readOperations, this, delegate(StringBuilder sb, IColumnModification o, DmUpdateSqlGenerator generator)
			{
				generator.SqlGenerationHelper.DelimitIdentifier(sb, o.ColumnName);
				if (generator.GetTypeNameForCopy(o.Property).Equals("INTEGER identity(1, 1)"))
				{
					sb.Append(" ").Append("integer");
				}
				else
				{
					sb.Append(" ").Append(generator.GetTypeNameForCopy(o.Property));
				}
			});
		commandStringBuilder.Append(")").Append(this.SqlGenerationHelper.StatementTerminator).AppendLine();
		commandStringBuilder.Append("TYPE ccc").Append(index).Append(" IS ARRAY rrr")
			.Append(index)
			.AppendLine("[];")
			.AppendLine()
			.Append("c")
			.Append(index)
			.Append(" ccc")
			.Append(index)
			.AppendLine("; ")
			.AppendLine();
	}

	private string GetTypeNameForCopy(IProperty property)
	{
		string text = RelationalPropertyExtensions.GetColumnType(property);
		if (text == null)
		{
			IProperty obj = property.FindFirstPrincipal();
			object obj2 = ((obj != null) ? RelationalPropertyExtensions.GetColumnType(obj) : null);
			if (obj2 == null)
			{
				RelationalTypeMapping obj3 = this.Dependencies.TypeMappingSource.FindMapping(((IReadOnlyPropertyBase)property).ClrType);
				obj2 = ((obj3 != null) ? obj3.StoreType : null);
			}
			text = (string)obj2;
		}
		if (((IReadOnlyPropertyBase)property).ClrType == typeof(byte[]) && text != null && (text.Equals("rowversion", StringComparison.OrdinalIgnoreCase) || text.Equals("timestamp", StringComparison.OrdinalIgnoreCase)))
		{
			if (!((IReadOnlyProperty)property).IsNullable)
			{
				return "binary(8)";
			}
			return "varbinary(8)";
		}
		return text;
	}

	public override void AppendBatchHeader(StringBuilder commandStringBuilder)
	{
		commandStringBuilder.AppendLine();
	}

	public override string GenerateNextSequenceValueOperation(string name, string schema)
	{
		StringBuilder stringBuilder = new StringBuilder();
		((UpdateSqlGenerator)this).AppendNextSequenceValueOperation(stringBuilder, name, schema);
		return stringBuilder.ToString();
	}

	public override void AppendNextSequenceValueOperation(StringBuilder commandStringBuilder, string name, string schema)
	{
		commandStringBuilder.Append("SELECT ");
		this.SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, name, schema);
		commandStringBuilder.Append(".NEXTVAL");
	}

	public override ResultSetMapping AppendInsertReturningOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, int commandPosition, out bool requiresTransaction)
	{
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		string tableName = command.TableName;
		string schema = command.Schema;
		IReadOnlyList<IColumnModification> columnModifications = command.ColumnModifications;
		List<IColumnModification> writeOperations = columnModifications.Where((IColumnModification o) => o.IsWrite).ToList();
		List<IColumnModification> list = columnModifications.Where((IColumnModification o) => o.IsRead).ToList();
		AppendInsertCommand(commandStringBuilder, tableName, schema, writeOperations);
		requiresTransaction = false;
		if (list.Count > 0)
		{
			List<IColumnModification> conditionOperations = columnModifications.Where((IColumnModification o) => o.IsKey).ToList();
			return AppendSelectAffectedCommand(commandStringBuilder, tableName, schema, list, conditionOperations, commandPosition);
		}
		return (ResultSetMapping)0;
	}

	protected override ResultSetMapping AppendUpdateReturningOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, int commandPosition, out bool requiresTransaction)
	{
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		string tableName = command.TableName;
		string schema = command.Schema;
		IReadOnlyList<IColumnModification> columnModifications = command.ColumnModifications;
		List<IColumnModification> writeOperations = columnModifications.Where((IColumnModification o) => o.IsWrite).ToList();
		List<IColumnModification> conditionOperations = columnModifications.Where((IColumnModification o) => o.IsCondition).ToList();
		List<IColumnModification> list = columnModifications.Where((IColumnModification o) => o.IsRead).ToList();
		requiresTransaction = false;
		AppendUpdateCommand(commandStringBuilder, tableName, schema, writeOperations, conditionOperations);
		if (list.Count > 0)
		{
			List<IColumnModification> conditionOperations2 = columnModifications.Where((IColumnModification o) => o.IsKey).ToList();
			return AppendSelectAffectedCommand(commandStringBuilder, tableName, schema, list, conditionOperations2, commandPosition);
		}
		return AppendSelectAffectedCountCommand(commandStringBuilder);
	}

	protected override ResultSetMapping AppendDeleteReturningOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, int commandPosition, out bool requiresTransaction)
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		string tableName = command.TableName;
		string schema = command.Schema;
		List<IColumnModification> conditionOperations = command.ColumnModifications.Where((IColumnModification o) => o.IsCondition).ToList();
		requiresTransaction = false;
		AppendDeleteCommand(commandStringBuilder, tableName, schema, conditionOperations);
		return AppendSelectAffectedCountCommand(commandStringBuilder);
	}

	protected void AppendInsertCommand(StringBuilder commandStringBuilder, string name, string? schema, IReadOnlyList<IColumnModification> writeOperations)
	{
		this.AppendInsertCommandHeader(commandStringBuilder, name, schema, writeOperations);
		this.AppendValuesHeader(commandStringBuilder, writeOperations);
		this.AppendValues(commandStringBuilder, name, schema, writeOperations);
		commandStringBuilder.AppendLine(this.SqlGenerationHelper.StatementTerminator);
	}

	protected void AppendUpdateCommand(StringBuilder commandStringBuilder, string name, string? schema, IReadOnlyList<IColumnModification> writeOperations, IReadOnlyList<IColumnModification> conditionOperations)
	{
		this.AppendUpdateCommandHeader(commandStringBuilder, name, schema, writeOperations);
		this.AppendWhereClause(commandStringBuilder, conditionOperations);
		commandStringBuilder.AppendLine(this.SqlGenerationHelper.StatementTerminator);
	}

	protected void AppendDeleteCommand(StringBuilder commandStringBuilder, string name, string? schema, IReadOnlyList<IColumnModification> conditionOperations)
	{
		this.AppendDeleteCommandHeader(commandStringBuilder, name, schema);
		this.AppendWhereClause(commandStringBuilder, conditionOperations);
		commandStringBuilder.AppendLine(this.SqlGenerationHelper.StatementTerminator);
	}

	protected virtual ResultSetMapping AppendSelectAffectedCommand(StringBuilder commandStringBuilder, string name, string? schema, IReadOnlyList<IColumnModification> readOperations, IReadOnlyList<IColumnModification> conditionOperations, int commandPosition)
	{
		AppendSelectCommandHeader(commandStringBuilder, readOperations);
		AppendFromClause(commandStringBuilder, name, schema);
		AppendWhereAffectedClause(commandStringBuilder, conditionOperations);
		commandStringBuilder.AppendLine(this.SqlGenerationHelper.StatementTerminator).AppendLine();
		return (ResultSetMapping)5;
	}

	protected void AppendSelectCommandHeader(StringBuilder commandStringBuilder, IReadOnlyList<IColumnModification> operations)
	{
		commandStringBuilder.Append("SELECT ").AppendJoin(operations, this.SqlGenerationHelper, delegate(StringBuilder sb, IColumnModification o, ISqlGenerationHelper helper)
		{
			helper.DelimitIdentifier(sb, o.ColumnName);
		});
	}

	protected void AppendFromClause(StringBuilder commandStringBuilder, string name, string? schema)
	{
		commandStringBuilder.AppendLine().Append("FROM ");
		this.SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, name, schema);
	}

	protected void AppendWhereAffectedClause(StringBuilder commandStringBuilder, IReadOnlyList<IColumnModification> operations)
	{
		commandStringBuilder.AppendLine().Append("WHERE ");
		AppendRowsAffectedWhereCondition(commandStringBuilder, 1);
		if (operations.Count <= 0)
		{
			return;
		}
		commandStringBuilder.Append(" AND ").AppendJoin(operations, delegate(StringBuilder sb, IColumnModification v)
		{
			if (v.IsKey && !v.IsRead)
			{
				this.AppendWhereCondition(sb, v, v.UseOriginalValueParameter);
			}
			else if (IsIdentityOperation(v))
			{
				AppendIdentityWhereCondition(sb, v);
			}
		}, " AND ");
	}

	protected void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder, int expectedRowsAffected)
	{
		commandStringBuilder.Append("sql%ROWCOUNT = ").Append(expectedRowsAffected.ToString(CultureInfo.InvariantCulture));
	}

	protected bool IsIdentityOperation(IColumnModification modification)
	{
		if (modification.IsKey)
		{
			return modification.IsRead;
		}
		return false;
	}

	protected void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, IColumnModification columnModification)
	{
		this.SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, columnModification.ColumnName);
		commandStringBuilder.Append(" = ");
		commandStringBuilder.Append("scope_identity()");
	}

	protected ResultSetMapping AppendSelectAffectedCountCommand(StringBuilder commandStringBuilder)
	{
		commandStringBuilder.Append("SELECT sql%ROWCOUNT").Append(this.SqlGenerationHelper.StatementTerminator).AppendLine()
			.AppendLine();
		return (ResultSetMapping)5;
	}
}
